/*
 * FitnessComputer.h
 *
 * Reprezentuje MPI Slave.
 *  Created on: 4.1.2013
 *      Author: Lukas Gemela
 */

#ifndef FITNESSCOMPUTER_H_
#define FITNESSCOMPUTER_H_
#include <iostream>
#include "MPITypes.h"
#include "EvoAlghorithm.h"
#include "IFastDataApprox.h"
#include "Checker.h"
#include "mpi.h"
class FitnessComputer {

public:

	FitnessComputer(const shared_ptr<IFastDataApprox> &BGValues,
			const shared_ptr<IFastDataApprox> &IGValues,
			const shared_ptr<vector<double>> p_ECGValues, INPUT_PARAMS params, int size) :
			p_BGValues(BGValues), p_IGValues(IGValues), p_ECGValues(p_ECGValues), params(
					params), checker(Checker(params)), poolSize(size) {
	}


	/**
	 * Spustí slave. Dokud nepřijde příkaz STOP, čeká na data, spočítá fitness a odevzdá
	 * výsledek masterovi.
	 */
	void compute( LPAPPROXPOINT bgAPPROXPOINTS, LPAPPROXPOINT igAPPROXPOINTS, int len) const {
		MPITypes mpiTypes;
		int area = params.populationSize / poolSize;
		INDIVIDUUM * subPopulation = new INDIVIDUUM[area];
		double * subFitness = new double[area];
		bool stop = false;
		while (!stop) {
			// přijmout práci

			MPI_Scatter(NULL, area, mpiTypes.getIndividuum(), subPopulation, area,
					mpiTypes.getIndividuum(), 0, MPI_COMM_WORLD);
			for (int i = 0; i < area; i++) {
				subFitness[i] = fitness(subPopulation[i], bgAPPROXPOINTS,
						igAPPROXPOINTS, len);
			}
			// poslat zpět do rootu
			MPI_Gather(subFitness, area, MPI_DOUBLE, NULL, area, MPI_DOUBLE, 0,
					MPI_COMM_WORLD);
			MPI_Bcast(&stop, 1, MPI_LOGICAL, 0, MPI_COMM_WORLD);
		}
		delete [] subPopulation;
		delete [] subFitness;
	}

private:
	/**
	 * Spočítá fitness pro dané individuum
	 */
	double fitness(INDIVIDUUM individuum, LPAPPROXPOINT bgAPPROXPOINTS,
			LPAPPROXPOINT igAPPROXPOINTS, int len) const {


		double p = individuum.p;
		double cg = individuum.cg;
		double c = individuum.c;
		double dt = individuum.dt;
		double k = individuum.k;

		int count = 0;
		double sum = 0;

		if (!countAverage(bgAPPROXPOINTS, igAPPROXPOINTS, p, cg, c, dt, k, len,
				&sum, &count)) {
			return 0;
		}
		//čím větší fitness, tím lepší výsledek
		return 1.0 / (sum / (double) count);

	}
	/**
	 * Spočítat průměrnou odchylku levé a pravé strany rovnice
	 */
	bool countAverage(LPAPPROXPOINT bgAPPROXPOINTS,
			LPAPPROXPOINT igAPPROXPOINTS, double p, double cg, double c,
			double dt, double k, int len, double* sum, int* count) const {

		double localSum = 0;
		int localCount = 0;
		for (int i = 0; i < len; i++) {

			int igPoint = 0;
			double ecg = (*p_ECGValues)[i];
			//kontrolujeme hodnoty (teoreticky by měly být vždy v mezích,
			//protože je kontrolujeme i při mutaci
			if (!checker.checkRangeC(c) || !checker.checkRangeCG(cg)
					|| !checker.checkRangeP(p) || !checker.checkRangeDT(dt)
					|| !checker.checkTimeJumpRange(k, dt, ecg)) {
				return false;
			}
			p_BGValues->GetPrecedingPoint(
					TimeUtils::convertSecToWin(i) + dt + k * dt * ecg,
					&igPoint);
			//Počítáme pouze na části dat, které máme naměřené
			if (igPoint < len && igPoint > 0) {
				double dif = fabs(
						p * bgAPPROXPOINTS[i].y + cg * bgAPPROXPOINTS[i].y * ecg
								+ c - igAPPROXPOINTS[igPoint].y);

				localCount++;
				localSum += dif;
			}

		}
		*count = localCount;
		*sum = localSum;
		return true;
	}
private:
	shared_ptr<IFastDataApprox> p_BGValues;
	shared_ptr<IFastDataApprox> p_IGValues;
	shared_ptr<vector<double>> p_ECGValues;
	INPUT_PARAMS params;
	Checker checker;
	int poolSize;
};

#endif /* FITNESSCOMPUTER_H_ */
